#!/usr/bin/perl -w
# $Rev: 11 $
# $Author: archon810 $
# $Date: 2012-07-25 05:42:52 +0000 (Wed, 25 Jul 2012) $
# $HeadURL: http://wp-copy-tool.googlecode.com/svn/trunk/wp-copy-tool.pl $
#
#  WP Copy Tool - http://beerpla.net/wordpress/wp-copy-tool
#								- http://code.google.com/p/wp-copy-tool/
#  Copyright (C) 2009 Artem Russakovskii
#
#  == BEGIN LICENSE ==
#
#  Licensed under the terms of GNU General Public License Version 2 or later (the "GPL")
#     http://www.gnu.org/licenses/gpl.html
#
#  == END LICENSE ==
#

use strict;

# this is needed so that the script can be run from other dirs
use FindBin;
use lib $FindBin::Bin;

use Utils;
use Settings;
use Term::Prompt;

#################
# INSTALL STEPS #
#################
# 1. Init, read settings, error check ONGOING
# 2. Copy db DONE
# 3. Change siteurl and home DONE
# 4. Set privacy DONE
# 5. Copy code DONE
# 6. Make sure the .htaccess and .htpasswd are there and if not, create them DONE
# 7. Change wp-config.php DONE
# 8. Turn off plugins DONE
# 9. Change background color MEH
# 10. Rename blog DONE

# some initial error checking and setup
sub init
{
	# print usage if input is incorrect
	usage() if scalar @ARGV < 1;

	# print all settings
	print "Settings used:\n";
	print "-" x 20 . "\n";
	while (my ($k,$v) = each %settings){
		printf("%-30s => %-30s\n", $k, ($k =~ /password/ ? "*" x length($v) : $v)); # do not print passwords
	}
	print "-" x 20 . "\n";

	# check for original dir
	if(!-e $settings{orig_wp_root_dir})
	{
		print RED, "[[FATAL ERROR]] Original dir $settings{orig_wp_root_dir} does not exist.\n", RESET and exit;
	}

	# check if the new dir is writable
	if(getpwuid($<) ne "root" && !-w $settings{new_wp_root_dir}){
		print RED, "[[FATAL ERROR]] New dir $settings{new_wp_root_dir} cannot be written. Please check permissions.\n", RESET and exit;
	}

	# check for mandatory programs
	foreach my $program (qw(mysql_bin_path mysqldump_bin_path rsync_bin_path sed_bin_path)){
		my $binary = $settings{$program};
		if(`which $binary  2>&1` =~ /which: no/i){
			print RED, "[[FATAL ERROR]] $binary doesn't seem to be installed. You can force its location in Settings.pm.\n", RESET and exit;
		}
	}

	# only one of these has to exist
	my $all_ok = 0;
	foreach my $program (qw(htpasswd_bin_path htpasswd2_bin_path)){
		my $binary = $settings{$program};
		if(`which $binary  2>&1` !~ /which: no/i){
			$all_ok = 1;
			$settings{htpasswd_bin_path} = $settings{$program};
			last;
		}
	}
	if(!$all_ok){
		print RED, "[[FATAL ERROR]] htpasswd doesn't seem to be installed. You can force its location in Settings.pm.\n", RESET and exit;
	}

	# check for uninitialized variables
	foreach(qw(
		orig_wp_host orig_wp_username orig_wp_password orig_wp_db_name orig_wp_root_dir
		new_wp_domain new_wp_host new_wp_username new_wp_password new_wp_db_name new_wp_root_dir
	)){
		if(is_empty($settings{$_})){
			print RED, "[[INIT REQUIRED]] $_ does not seem to be initialized. Have you edited your Settings.pm?\n", RESET and exit;
		}
	}
}

# finalize
sub finalize
{
	print GREEN, "DONE!\n", RESET;
}

# copy the wp dir
sub copy_wp_dir
{
	&func_start;
	my $cmd = "rsync -a --delete --progress --stats " . Cwd::realpath($settings{orig_wp_root_dir}) . "/ " . Cwd::realpath($settings{new_wp_root_dir});
	eval{
		foreach(@{$settings{exclude_dirs}}){
			$cmd .= " --exclude $_";
		}
	};
	system_cmd($cmd);

	unlink(Cwd::realpath("$settings{new_wp_root_dir}/wp-content/advanced-cache.php")); # wp-super-cache likes to write hardcoded crap to this
}

# copy the wp database
sub copy_wp_db
{
	&func_start;
	my $cmd = "mysqldump -h'$settings{orig_wp_host}' --opt --skip-lock-tables -u'$settings{orig_wp_username}' -p'$settings{orig_wp_password}' -P$settings{orig_wp_port} $settings{orig_wp_db_name}";
	$cmd .= "| mysql -h'$settings{new_wp_host}' -u'$settings{new_wp_username}' -p'$settings{new_wp_password}' -P$settings{new_wp_port} $settings{new_wp_db_name}";

	system_cmd($cmd);
}

# update wp db options
sub update_db_options
{
	&func_start;

	my $dbh = db_connect($settings{new_wp_host}, $settings{new_wp_db_name}, $settings{new_wp_username}, $settings{new_wp_password}, $settings{new_wp_port}, $settings{max_retries});

	# update siteurl and home options (alternatively, these could be modified in the cfg file, see http://codex.wordpress.org/Editing_wp-config.php#WordPress_address_.28URL.29)
	my $sql = "UPDATE $settings{orig_wp_options_table} SET option_value=" . $dbh->quote($settings{new_wp_domain}) . " WHERE option_name IN ('siteurl', 'home')";
	eval{$dbh->do($sql)};
	print RED, "Could not update the database $settings{new_wp_db_name}: $@\n", RESET and exit if $@;


	# update the blog_public setting and turn it off
	$sql = "UPDATE $settings{orig_wp_options_table} SET option_value=0 WHERE option_name = 'blog_public'";
	eval{$dbh->do($sql)};
	print RED, "Could not update the database $settings{new_wp_db_name}: $@\n", RESET and exit if $@;

	# update the blogname setting
	$sql = "UPDATE $settings{orig_wp_options_table} SET option_value=" . $dbh->quote($settings{new_wp_blog_name}) . " WHERE option_name = 'blogname'";
	eval{$dbh->do($sql)};
	print RED, "Could not update the database $settings{new_wp_db_name}: $@\n", RESET and exit if $@;

	# update other settings
	$sql = "UPDATE $settings{orig_wp_options_table} SET option_value=replace(option_value, " . $dbh->quote($settings{orig_wp_root_dir}) . ", " . $dbh->quote($settings{new_wp_root_dir}) . ") WHERE option_name IN('upload_path')";
	eval{$dbh->do($sql)};
	print RED, "Could not update the database $settings{new_wp_db_name}: $@\n", RESET and exit if $@;

	print GREEN, "Success!\n", RESET;
}

# disable some plugins
sub disable_plugins
{
	&func_start;

	foreach my $plugin (@{$settings{plugins_to_deactivate}}){
		print "Deactivating $plugin by moving it to ${plugin}_disabled.\n";
		my $plugin_dir_old = Cwd::realpath("$settings{new_wp_root_dir}/wp-content/plugins/$plugin");
		my $plugin_dir_new = Cwd::realpath("$settings{new_wp_root_dir}/wp-content/plugins/${plugin}_disabled");

		my $cmd = "mv $plugin_dir_old $plugin_dir_new";

		if(!-e $plugin_dir_old){
			print YELLOW, "$plugin_dir_old was not found. Skipping.\n", RESET;
		}
		elsif($plugin_dir_old && $plugin_dir_new && !system_cmd($cmd) && -e $plugin_dir_new && !-e $plugin_dir_old){
			print "$plugin_dir_old ", GREEN, "successfully moved", RESET, " to $plugin_dir_new.\n";
		}
		else{
			print RED, "Failed to move ", RESET, "$plugin_dir_old to $plugin_dir_new.\n";
		}
	}
}

# edit wp config with new values
sub edit_wp_config
{
	&func_start;

	my $wp_config_file = Cwd::realpath("$settings{new_wp_root_dir}/wp-config.php");
	foreach my $wp_setting (
		["DB_NAME" => $settings{new_wp_db_name}],
		["DB_USER" => $settings{new_wp_username}],
		["DB_PASSWORD" => $settings{new_wp_password}],
		["DB_HOST" => $settings{new_wp_host} . ($settings{new_wp_port} == 3306 ? "" : ":$settings{new_wp_port}")]
	){
		my $cmd = q!sed -i -r "s/(\b! . $wp_setting->[0] . q!\b.*?,\s*['\"]).*?(['\"].*?;)/\1! . $wp_setting->[1] . q!\2/" ! . $wp_config_file;
		system_cmd($cmd);
	}
}

# htpassword protect the blog
sub ht_protect
{
	&func_start;

	print YELLOW, "Skipping ht_protect\n" and return if is_empty($settings{new_wp_ht_passwd_path}) || is_empty($settings{new_wp_ht_username}) || is_empty($settings{new_wp_ht_password});

	# create or modify the file containing the ht password
	my $cmd = "$settings{htpasswd_bin_path} ";

	# create the file if it doesn't exist
	if(!-e "$settings{new_wp_ht_passwd_path}"){
		$cmd .= "-c ";
	}

	$cmd .= "-bm $settings{new_wp_ht_passwd_path} $settings{new_wp_ht_username} $settings{new_wp_ht_password}";
	system_cmd($cmd);

	local $/;
	my $htaccess_file = "$settings{new_wp_root_dir}/.htaccess";
	if(!open(HTACCESS, "+<$htaccess_file") && !open(HTACCESS, "+>$htaccess_file")){
		print RED, "Error opening $htaccess_file. Check file permissions.\n";
	}
	else
	{
		my $file_contents = <HTACCESS>;

		if($file_contents !~ /(?<!#)AuthUserFile/i){
			print HTACCESS <<HT

AuthName "$settings{new_wp_blog_name}"
AuthType Basic
AuthUserFile $settings{new_wp_ht_passwd_path}
Require valid-user
HT
		}
		else
		{
			print YELLOW, "File $htaccess_file already contains authentication directives (AuthUserFile). No further action needed.\n";
		}

		close HTACCESS;
	}
}

#########
# BEGIN #
#########

init();
copy_wp_dir();
copy_wp_db();
update_db_options();
disable_plugins();
edit_wp_config();
ht_protect();
finalize();

